/**   
 * @Title: Method.java 
 * @Package net.gdface.codegen.wsdl 
 * @Description:  
 * @author guyadong   
 * @date 2015年7月14日 上午9:34:17 
 * @version V1.0   
 */
package net.gdface.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * @author guyadong
 *
 */
public class MethodDecorator {
	public class Parameter {
		public final Class<?> type;
		public final String name;
		public final Type genericType;
		private final Map<Class<? extends Annotation>, Annotation> annotation;

		private Parameter(String name, Class<?> type, Type genericType,
				Map<Class<? extends Annotation>, Annotation> annotation) {
			this.name = name;
			this.type = type;
			this.genericType = genericType;
			this.annotation = annotation;
		}

		/**
		 * 返回指定注释对象,没有定义注释则返回{@code null}
		 * @param annotationClass
		 * @return 注释对象
		 * @see java.util.Map#get(java.lang.Object)
		 */
		@SuppressWarnings("unchecked")
		public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
			return (T) annotation.get(checkNotNull(annotationClass, "annotationClass is null"));
		}

		/**
		 * 返回所有注释对象
		 * @return 注释对象集合
		 * @see java.util.Map#values()
		 */
		public Collection<Annotation> getAnnotations() {
			return annotation.values();
		}

		/**
		 * @return type
		 */
		public Class<?> getType() {
			return type;
		}

		/**
		 * @return name
		 */
		public String getName() {
			return name;
		}

		/**
		 * @return genericType
		 */
		public Type getGenericType() {
			return genericType;
		}
	}

	protected final Method method;
	protected final String[] parameterNames;
	protected final Map<String, Parameter> paramMap;
	protected final Parameter[] parameters;
	protected final String docSignature;
	/**
	 * 构造方法<br>
	 * 从{@link Method}构造对象
	 * @param method
	 */
	public MethodDecorator(Method method){
		this(method, null);
	}
	/**
	 * 构造方法<br>
	 * 从{@link Method}构造对象
	 * @param method 方法对象
	 * @param parameterNames 方法的参数名,为{@code null}则将参数名自动按arg0...argN格式填充.
	 */
	public MethodDecorator(Method method, String[] parameterNames) {
		Class<?>[] types = checkNotNull(method, "method is null").getParameterTypes();
		if (null != parameterNames && parameterNames.length != types.length){
			throw new IllegalArgumentException("parameterNames length not equals actually parameter number");
		}
		this.method = method;
		if (null == parameterNames) {
			parameterNames = new String[types.length];
			for (int i = 0; i < parameterNames.length; i++)
				parameterNames[i] = String.format("arg%d", i);
		}
		this.parameterNames = parameterNames;
		this.parameters = createParameters();
		paramMap = createParamMap(parameters);
		docSignature = getDocSignature(true);	

	}

	private Parameter[] createParameters() {
		Parameter[] parameters = new Parameter[this.parameterNames.length];
		Annotation[][] parameterAnnotations = this.getParameterAnnotations();
		Class<?>[] parameterTypes = this.getParameterTypes();
		Type[] genericParameterTypes = this.getGenericParameterTypes();
		for (int i = 0; i < parameters.length; i++) {
			parameters[i] = new Parameter(parameterNames[i], parameterTypes[i], genericParameterTypes[i],
					getParameterAnnotationMap(parameterAnnotations[i]));
		}
		return parameters;
	}

	private final Map<String, Parameter> createParamMap(Parameter[] parameters) {
		HashMap<String, Parameter> map = new HashMap<String, Parameter>(parameters.length);
		for (Parameter p : parameters)
			map.put(p.name, p);
		return map;
	}

	private Map<Class<? extends Annotation>, Annotation> getParameterAnnotationMap(Annotation[] parameterAnnotations) {
		Map<Class<? extends Annotation>, Annotation> map = new HashMap<Class<? extends Annotation>, Annotation>(
				parameterAnnotations.length);
		for (Annotation annotation : parameterAnnotations)
			map.put(annotation.annotationType(), annotation);
		return map;
	}

	/**
	 * @param flag
	 * @throws SecurityException
	 * @see java.lang.reflect.AccessibleObject#setAccessible(boolean)
	 */
	public void setAccessible(boolean flag) throws SecurityException {
		method.setAccessible(flag);
	}

	/**
	 * @return the value of the object's accessible flag
	 * @see java.lang.reflect.AccessibleObject#isAccessible()
	 */
	public boolean isAccessible() {
		return method.isAccessible();
	}

	/**
	 * @return an object representing the declaring class of the underlying member
	 * @see Method#getDeclaringClass()
	 */
	public Class<?> getDeclaringClass() {
		return method.getDeclaringClass();
	}

	/**
	 * @return the simple name of the underlying member
	 * @see Method#getName()
	 */
	public String getName() {
		return method.getName();
	}

	/**
	 * @return the Java language modifiers for the underlying member
	 * @see Method#getModifiers()
	 */
	public int getModifiers() {
		return method.getModifiers();
	}

	/**
	 * @param annotationClass
	 * @return true if an annotation for the specified annotation type is present on this element, else false
	 * @see java.lang.reflect.AccessibleObject#isAnnotationPresent(java.lang.Class)
	 */
	public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
		return method.isAnnotationPresent(annotationClass);
	}

	/**
	 * @return an array of TypeVariable objects that represent the type variables declared by this generic declaration
	 * @see Method#getTypeParameters()
	 */
	public TypeVariable<Method>[] getTypeParameters() {
		return method.getTypeParameters();
	}

	/**
	 * @return all annotations present on this element
	 * @see java.lang.reflect.AccessibleObject#getAnnotations()
	 */
	public Annotation[] getAnnotations() {
		return method.getAnnotations();
	}

	/**
	 * @return the return type for the method this object represents
	 * @see Method#getReturnType()
	 */
	public Class<?> getReturnType() {
		return method.getReturnType();
	}

	/**
	 * @return a Type object that represents the formal return type of the underlying method
	 * @see Method#getGenericReturnType()
	 */
	public Type getGenericReturnType() {
		return method.getGenericReturnType();
	}

	/**
	 * @return the parameter types for the method this object represents
	 * @see Method#getParameterTypes()
	 */
	public Class<?>[] getParameterTypes() {
		return method.getParameterTypes();
	}

	/**
	 * @return an array of Types that represent the formal parameter types of the underlying method, in declaration order
	 * @see Method#getGenericParameterTypes()
	 */
	public Type[] getGenericParameterTypes() {
		return method.getGenericParameterTypes();
	}

	/**
	 * @return the exception types declared as being thrown by the method this object represents
	 * @see Method#getExceptionTypes()
	 */
	public Class<?>[] getExceptionTypes() {
		return method.getExceptionTypes();
	}

	/**
	 * @return an array of Types that represent the exception types thrown by the underlying method
	 * @see Method#getGenericExceptionTypes()
	 */
	public Type[] getGenericExceptionTypes() {
		return method.getGenericExceptionTypes();
	}

	/**
	 * @param obj
	 * @return true if this object is the same as the obj argument; false otherwise.
	 * @see Method#equals(java.lang.Object)
	 */
	public boolean equals(Object obj) {
		return method.equals(obj);
	}

	/**
	 * @return a hash code value for this object.
	 * @see Method#hashCode()
	 */
	public int hashCode() {
		return method.hashCode();
	}

	/**
	 * @param obj
	 * @param args
	 * @return the result of dispatching the method represented by this object on obj with parameters args
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @see Method#invoke(java.lang.Object, java.lang.Object[])
	 */
	public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,
			InvocationTargetException {
		return method.invoke(obj, args);
	}

	/**
	 * @return true if and only if this method is a bridge method as defined by the Java Language Specification
	 * @see Method#isBridge()
	 */
	public boolean isBridge() {
		return method.isBridge();
	}

	/**
	 * @return true if an only if this method was declared to take a variable number of arguments.
	 * @see Method#isVarArgs()
	 */
	public boolean isVarArgs() {
		return method.isVarArgs();
	}

	/**
	 * @return true if and only if this member was introduced by the compiler.
	 * @see Method#isSynthetic()
	 */
	public boolean isSynthetic() {
		return method.isSynthetic();
	}

	/**
	 * @param annotationClass
	 * @return this element's annotation for the specified annotation type if present on this element, else null
	 * @see Method#getAnnotation(java.lang.Class)
	 */
	public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
		return method.getAnnotation(annotationClass);
	}

	/**
	 * @return All annotations directly present on this element
	 * @see Method#getDeclaredAnnotations()
	 */
	public Annotation[] getDeclaredAnnotations() {
		return method.getDeclaredAnnotations();
	}

	/**
	 * @return the default value for the annotation member represented by this Method instance
	 * @see Method#getDefaultValue()
	 */
	public Object getDefaultValue() {
		return method.getDefaultValue();
	}

	/**
	 * @return an array of arrays that represent the annotations on the formal parameters, in declaration order, of the method represented by this Method object
	 * @see Method#getParameterAnnotations()
	 */
	public Annotation[][] getParameterAnnotations() {
		return method.getParameterAnnotations();
	}

	/**
	 * 获取参数名对应的{@link Parameter}对象,参数名无效返回{@code null}
	 * @param name
	 * @return {@link Parameter}对象
	 * @see java.util.Map#get(java.lang.Object)
	 */
	public Parameter getParameter(String name) {
		return paramMap.get(name);
	}

	/**
	 * @return parameterNames
	 */
	public String[] getParameterNames() {
		return parameterNames;
	}

	private static final int LANGUAGE_MODIFIERS = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE
			| Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED | Modifier.NATIVE;

	/**
	 * 
	 * @return 方法描述字符串
	 * @see Method#toGenericString()
	 */
	public String toGenericString() {
		return toGenericString(false);
	}

	/**
	 * 输出类名简写的(泛型)方法描述字符串
	 * 
	 * @param fullClassName
	 *            是否显示类的全名
	 * @return 方法描述字符串
	 * @see Method#toGenericString()
	 */
	public String toGenericString(boolean fullClassName) {
		try {
			StringBuilder sb = new StringBuilder();
			int mod = getModifiers() & LANGUAGE_MODIFIERS;
			if (mod != 0) {
				sb.append(Modifier.toString(mod) + " ");
			}
			Type[] typeparms = getTypeParameters();
			if (typeparms.length > 0) {
				boolean first = true;
				sb.append("<");
				for (Type typeparm : typeparms) {
					if (!first)
						sb.append(",");
					sb.append(TypeUtils.getTypeName(typeparm, fullClassName));
					/*
					 * if (typeparm instanceof Class) sb.append(((Class<?>) typeparm).getName()); else
					 * sb.append(typeparm.toString());
					 */
					first = false;
				}
				sb.append("> ");
			}

			Type genRetType = getGenericReturnType();
			sb.append(TypeUtils.getTypeName(genRetType, fullClassName) + " ");
			if (fullClassName)
				sb.append(TypeUtils.getTypeName(getDeclaringClass(), fullClassName) + ".");
			sb.append(getName() + "(");
			Type[] params = getGenericParameterTypes();
			for (int j = 0; j < parameterNames.length; j++) {
				sb.append(TypeUtils.getTypeName(params[j], fullClassName) + " ");
				sb.append(parameterNames[j]);
				if (j < (parameterNames.length - 1))
					sb.append(",");
			}
			sb.append(")");
			Type[] exceptions = getGenericExceptionTypes();
			if (exceptions.length > 0) {
				sb.append(" throws ");
				for (int k = 0; k < exceptions.length; k++) {
					sb.append(TypeUtils.getTypeName(exceptions[k], fullClassName));
					if (k < (exceptions.length - 1))
						sb.append(",");
				}
			}
			return sb.toString();
		} catch (Exception e) {
			return "<" + e + ">";
		}
	}

	/**
	 * 输出类名简写的方法描述字符串
	 * 
	 * @return 方法描述字符串
	 * @see #toString(boolean)
	 * @see Method#toString()
	 */
	@Override
	public String toString() {
		return toString(false);
	}

	/**
	 * 是否显示类的全名
	 * 
	 * @param fullClassName
	 * @return 类的全名字符串
	 * @see Method#toString()
	 */
	public String toString(boolean fullClassName) {
		try {
			StringBuffer sb = new StringBuffer();
			int mod = getModifiers() & LANGUAGE_MODIFIERS;
			if (mod != 0) {
				sb.append(Modifier.toString(mod) + " ");
			}
			sb.append(TypeUtils.getTypeName(getReturnType(), fullClassName) + " ");
			if (fullClassName)
				sb.append(TypeUtils.getTypeName(getDeclaringClass(), fullClassName) + ".");
			sb.append(getName() + "(");
			Class<?>[] params = getParameterTypes(); // avoid clone
			for (int j = 0; j < params.length; j++) {
				sb.append(TypeUtils.getTypeName(params[j], fullClassName) + " ");
				sb.append(parameterNames[j]);
				if (j < (parameterNames.length - 1))
					sb.append(",");
			}
			sb.append(")");
			Class<?>[] exceptions = (Class[]) getGenericExceptionTypes(); // avoid clone
			if (exceptions.length > 0) {
				sb.append(" throws ");
				for (int k = 0; k < exceptions.length; k++) {
					sb.append(exceptions[k].getName());
					if (k < (exceptions.length - 1))
						sb.append(",");
				}
			}
			return sb.toString();
		} catch (Exception e) {
			return "<" + e + ">";
		}
	}

	/**
	 * @return parameters
	 */
	public Parameter[] getParameters() {
		return parameters;
	}

	public final String getDocSignature(boolean fn) {
		return TypeUtils.getDocSignature(method, fn);
	}

	public String getDocSignature(final Map<String, Class<?>> importedList) {
		return TypeUtils.getDocSignature(method, importedList);
	}

	public Set<Type> getGenericExceptionTypeSet() {
		return new HashSet<Type>(Arrays.asList(getGenericExceptionTypes()));
	}

	/**
	 * @return docSignature
	 */
	public final String getDocSignature() {
		return docSignature;
	}

	/**
	 * @return docSignature
	 */
	public final String getSignature() {
		return TypeUtils.getSignature(method);
	}

	public Method delegate() {
		return method;
	}
}
